package view;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.zip.DataFormatException;

import javax.swing.ButtonGroup;
import javax.swing.DefaultCellEditor;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.table.TableColumn;

import controller.FirewallSession;

import server.Protocol;
import server.Rule;
import server.RulesTableType;
import server.Rule.typeOpt;

/**
 * Class is responsible for rule add/edit frame.
 * Frame contains: 	tables - for rule inserting.
 * 					rule type radio buttons (CONTEXT/IP).
 * 					rule direction radio buttons (IN/OUT).
 * 					standart buttons.
 */
public abstract class AbstractRuleEditTable  extends AbstractCommonFrame
{
	/**
	 * Server controller. 
	 */
	protected FirewallSession UI;
	
	/**
	 * Rule table.
	 */
	protected RuleTable rule = new RuleTable();
	
	/**
	 * Rule direction options names. 
	 */
	private final String[] directionRadioNames = 
		{RulesTableType.IN.toString(), RulesTableType.OUT.toString(), "Both"};
	
	/**
	 * Rule direction (IN/OUT/BOTH) radio buttons. 
	 */
	protected JRadioButton[] directionRadio = new JRadioButton[directionRadioNames.length];
	
	/**
	 * Rule types options names 
	 */
	private final String[] typesRadioNames = {"Ip rule", "Context rule"};
	
	/**
	 * Rule type (CONTEXT/IP) radio buttons
	 */
	protected JRadioButton[] typesRadio = new JRadioButton[typesRadioNames.length];
	
	/**
	 * IP rule action options names
	 */
	private final String[] ipActionNames = 
		{Rule.permissionOpt.ALLOW.toString(), Rule.permissionOpt.DENY.toString()};

	/**
	 * Context rule action options names
	 */
	private final String[] protocolActionNames = 
		{Rule.permissionOpt.DENY.toString()};

	/**
	 * Current rule type
	 */
	private typeOpt ruleType = null;
	
	/**
	 * C'tor - Frame with specified name and controller is created. 
	 * @param title - the frame name
	 * @param ui	- the controller for the system actions
	 */
	public AbstractRuleEditTable(String title, FirewallSession ui)
	{
		/* Frame initialization */
		super(title);
		this.UI = ui;
	    setLayout(new BorderLayout());
	    
		/* Label adding */
		JPanel choices = new JPanel(new BorderLayout());
		JLabel label = new JLabel(getTableLabel());
		choices.add(label, BorderLayout.NORTH);

		/*** Rule type radio buttons ***/
		ButtonGroup typesGroup = new ButtonGroup();
		JPanel types = new JPanel(new GridLayout(1,typesRadio.length));
		
		/* Add all radio buttons to group and to the window */
		for(int i = 0; i < typesRadio.length; i++)
		{
			typesRadio[i] = new JRadioButton();
			typesRadio[i].setText(typesRadioNames[i]);
			typesGroup.add(typesRadio[i]);
			types.add(typesRadio[i]);
			
			/* Add for each type radio button listener with appropriate function */
			typesRadio[i].addActionListener(new ActionListener()
			{
				public void actionPerformed(ActionEvent e)
				{
					ruleTypeSelection();
				}
			});
		}
		choices.add(types, BorderLayout.CENTER);
		
		/*** Direction radio buttons ***/
		ButtonGroup group = new ButtonGroup();
		JPanel directions = new JPanel(new GridLayout(1,directionRadio.length));
		
		/* Add all radio buttons to group and to the window.
		 * Buttons are disabled till the rule type is choosen. */
		for(int i = 0; i < directionRadio.length; i++)
		{
			directionRadio[i] = new JRadioButton();
			directionRadio[i].setText(directionRadioNames[i]);
			directionRadio[i].setEnabled(false);
			group.add(directionRadio[i]);
			directions.add(directionRadio[i]);	
		}
		choices.add(directions, BorderLayout.SOUTH);
		add(choices, BorderLayout.NORTH);
		
		/*** Table treatment ***/
		JPanel table = new JPanel();
		
		/* Header adding */
		table.setLayout(new BorderLayout());		
		table.add(rule.getRulesTableHeader(), BorderLayout.NORTH);
		rule.getTableHeader().setReorderingAllowed(false);
	    tableUpdates();
	    /* Buttons are disabled till the rule type is choosen. */
		rule.setEnabled(false);
		
		/* Table adding */
		JScrollPane scrollpane = new JScrollPane(rule);
		scrollpane.getViewport().setPreferredSize(new Dimension(30,30));
		table.add(scrollpane,  BorderLayout.CENTER);

		add(table, BorderLayout.CENTER);
	    addStandartButtons();
	}
	
	/**
	 * @return the table title.
	 */
	protected abstract String getTableLabel();

	/**
	 * Action performed by finish button press:
	 * new rule is passed to FW, tables are updated 
	 */
	protected void finishAction()
	{
		try{
			/* Check that rule type (IP/CONTEXT) was defined */
			if(ruleType == null)
			{
				ErrorFrame.AddError("Fill the table", this);
				return;
			}
			
			/* Check the selected direction */
			if(directionRadio[0].isSelected()) /* IN direction */
			{
				/* Build the IN rule, execute appropriate action */
				Rule new_ruleIn = rule.getRule(ruleType, RulesTableType.IN);
				ruleTreatment(RulesTableType.IN, new_ruleIn);
			}
			else if(directionRadio[1].isSelected()) /* OUT direction */
			{
				/* Build the OUT rule, execute appropriate action */
				Rule new_ruleOut = rule.getRule(ruleType, RulesTableType.OUT);
				ruleTreatment(RulesTableType.OUT, new_ruleOut);			
			}
			else if(directionRadio[2].isSelected()) /* BOTH directions */
			{
				/* Build the IN and OUT rules, execute appropriate actions */
				Rule new_ruleIn = rule.getRule(ruleType, RulesTableType.IN);
				Rule new_ruleOut = rule.getRule(ruleType, RulesTableType.OUT);				
				ruleTreatment(RulesTableType.IN,new_ruleIn);
				ruleTreatment(RulesTableType.OUT,new_ruleOut);
			}
			
			/* If there were no exceptions - close the window */
			dispose();
			
		} catch (DataFormatException e) {
			/* In case the rule wasn't entered in the right way,
			 * No exit from the frame, no action performed */
		}
	}

	/**
	 * Action performed by rule type selection.
	 */
	protected void ruleTypeSelection()
	{
		/* Enable the actions */
		rule.setEnabled(true);
		for(int i = 0; i < directionRadio.length; i++)
		{
			directionRadio[i].setEnabled(true);
		}
		
		/* Ip rule */
		if(typesRadio[0].isSelected())
		{
			/* Define the rule type */
			ruleType = typeOpt.IP;
			/* Disable protocol field */
			rule.setValueAt("", 0, RuleTable.protocolColumnIndex);
			rule.setDisabled(RuleTable.protocolColumnIndex);

			/* Combo box update */
			createActionComboBox(ipActionNames);
		}
		else if(typesRadio[1].isSelected())
		{
			/* Define the rule type */
			ruleType = typeOpt.CONTEXT;
			/* Disable IP field */
			rule.setValueAt("", 0, RuleTable.ipColumnIndex);
			rule.setDisabled(RuleTable.ipColumnIndex);
			
			/* Combo box update */
			createActionComboBox(protocolActionNames);
		}		
	}
	
	/**
	 * Action performed by finish button press.
	 */
	protected abstract void ruleTreatment(RulesTableType ruleType, Rule new_rule);
	
	/**
	 * Combo boxes are added where user has no unlimitted choices. 
	 */
	private void tableUpdates()
	{
		/* Table comboboxes adding */
		createActionComboBox(ipActionNames);
		createProtocolComboBox();
	}
	
	/**
	 * Combo box is added for rule protocols (protocols that defined in the system). 
	 */
	private void createProtocolComboBox()
	{
		/* Get the relevant column */
		TableColumn protocolColumn = rule.getColumnModel().getColumn(RuleTable.protocolColumnIndex);
		/* Create the empty combobox */
		JComboBox protocolComboBox = null;
		
		/* Update the combo box */
		try
		{
			/* Create the list of protocols */
			Protocol[] protocols = UI.getProtocols();
			String[] protocolsChoices = new String[protocols.length + 1];
			protocolsChoices[0] = "";
			for(int i = 0; i < protocols.length; i++)
			{
				protocolsChoices[i+1] = protocols[i].protocol;
			}
			protocolComboBox = new JComboBox(protocolsChoices);
		} catch (Exception e) {
			ErrorFrame.AddError("RMI connection problems: protocols are unknown. Try later.", null);
		}
		
		/* Add the combo box to the table, if the protocols were defined */
		if(protocolComboBox != null)
		{
			protocolColumn.setCellEditor(new DefaultCellEditor(protocolComboBox));
		}
	}
	
	/**
	 * Combo box is added for rule action (ALLOW/DENY). 
	 */
	private void createActionComboBox(String[] actions)
	{
		/* Get the relevant column */
		TableColumn actionColumn = rule.getColumnModel().getColumn(RuleTable.actionColumnIndex);
		/* Create the empty combobox */
		JComboBox actionComboBox = new JComboBox(actions);
		/* Add the combo box to the table */
		actionColumn.setCellEditor(new DefaultCellEditor(actionComboBox));		
	}
}
